package com.panasonic.web.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.panasonic.web.domain.cms.*;
import com.panasonic.web.mapper.*;
import com.panasonic.web.model.enums.CmsExamRecordStatus;
import com.panasonic.web.model.enums.CmsExamScoreStatus;
import com.panasonic.web.service.ICmsExamRecordService;
import com.panasonic.web.service.ICmsExamService;
import com.panasonic.web.util.CmsSecurityUtils;
import com.panasonic.web.util.NumberUtil;
import com.panasonic.web.vo.*;
import com.panasonic.common.core.redis.RedisCache;
import com.panasonic.common.exception.CustomException;
import com.panasonic.common.utils.DateUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 考试记录Service业务层处理
 *
 * @author lwf
 * @date 2021-11-16
 */
@Service
public class CmsExamRecordServiceImpl implements ICmsExamRecordService {

    private static final String EXAM_START = "exam_start_";


    @Autowired
    private CmsExamMapper cmsExamMapper;
    @Autowired
    private CmsExamRecordMapper cmsExamRecordMapper;

    @Autowired
    private CmsExamQuestionMapper cmsExamQuestionMapper;

    @Autowired
    private CmsExamScoreMapper cmsExamScoreMapper;

    @Autowired
    private CmsExamExamineeDao cmsExamExamineeDao;

    @Autowired
    private ICmsExamService iCmsExamService;
    @Autowired
    private RedisCache redisCache;

    /**
     * 查询考试记录
     *
     * @param id 考试记录ID
     * @return 考试记录
     */
    @Override
    public CmsExamRecord selectCmsExamRecordById(Long id) {
        return cmsExamRecordMapper.selectCmsExamRecordById(id);
    }

    /**
     * 查询考试记录列表
     *
     * @param cmsExamRecord 考试记录
     * @return 考试记录
     */
    @Override
    public List<CmsExamRecord> selectCmsExamRecordList(CmsExamRecord cmsExamRecord) {
        return cmsExamRecordMapper.selectCmsExamRecordList(cmsExamRecord);
    }

    /**
     * 新增考试记录
     *
     * @param cmsExamRecord 考试记录
     * @return 结果
     */
    @Transactional
    @Override
    public int insertCmsExamRecord(CmsExamRecord cmsExamRecord) {
        cmsExamRecord.setCreateBy(CmsSecurityUtils.getUsername());
        cmsExamRecord.setCreateTime(DateUtils.getNowDate());
        return cmsExamRecordMapper.insertCmsExamRecord(cmsExamRecord);
    }


    /**
     * 修改考试记录
     *
     * @param cmsExamRecord 考试记录
     * @return 结果
     */
    @Override
    public int updateCmsExamRecord(CmsExamRecord cmsExamRecord) {
        cmsExamRecord.setUpdateTime(DateUtils.getNowDate());
        return cmsExamRecordMapper.updateCmsExamRecord(cmsExamRecord);
    }

    /**
     * 批量删除考试记录
     *
     * @param ids 需要删除的考试记录ID
     * @return 结果
     */
    @Override
    public int deleteCmsExamRecordByIds(Long[] ids) {
        return cmsExamRecordMapper.deleteCmsExamRecordByIds(ids);
    }

    /**
     * 删除考试记录信息
     *
     * @param id 考试记录ID
     * @return 结果
     */
    @Override
    public int deleteCmsExamRecordById(Long id) {
        return cmsExamRecordMapper.deleteCmsExamRecordById(id);
    }

    @Override
    @Transactional(rollbackFor = CustomException.class)
    public void batchInsert(CmsExamRecordBatchVo cmsExamRecordBatchVo) {
        checkOverExam(cmsExamRecordBatchVo.getExamId());
        CmsExamQuestion cmsExamQuestion = new CmsExamQuestion();
        cmsExamQuestion.setExamId(cmsExamRecordBatchVo.getExamId());
        List<CmsExamQuestion> cmsExamQuestions = cmsExamQuestionMapper.selectCmsExamQuestionList(cmsExamQuestion);
        if (CollectionUtils.isEmpty(cmsExamQuestions)) {
            throw new CustomException("找不到试卷");
        }
        List<CmsExamRecord> cmsExamRecordList = cmsExamRecordBatchVo.getCmsExamRecordList();
        cmsExamRecordList.stream().forEach(s -> {
            s.setCreateBy(CmsSecurityUtils.getUsername());
            s.setCreateTime(DateUtils.getNowDate());
            s.setStatus(CmsExamRecordStatus.INIT.getType());
            s.setUserId(CmsSecurityUtils.getUserId());
            s.setExamId(cmsExamRecordBatchVo.getExamId());
            s.setScoreId(cmsExamRecordBatchVo.getScoreId());
            Optional<CmsExamQuestion> question = cmsExamQuestions.stream()
                    .filter(q -> q.getId().equals(s.getQuestionId()) && q.getType() != 3)
                    .findFirst();
            if (question.isPresent()) {
                if (question.get().getAnswer().equals(s.getAnswer())) {
                    s.setScore(question.get().getScore());
                } else {
                    s.setScore(0);
                }
            }
            cmsExamRecordMapper.insertCmsExamRecord(s);
        });
        CmsExamScore cmsExamScore = cmsExamScoreMapper.getScoreByExamIdAndUserid(cmsExamRecordBatchVo.getExamId(), CmsSecurityUtils.getUserId());
        cmsExamScore.setExamStatus(CmsExamScoreStatus.FINISHED.getStatus());
        cmsExamScore.setUpdateBy(CmsSecurityUtils.getUsername());
        cmsExamScore.setFinishTime(DateUtils.getNowDate());
        cmsExamScore.setUpdateTime(DateUtils.getNowDate());
        cmsExamScoreMapper.updateCmsExamScore(cmsExamScore);
        String key = getStartExamKey(cmsExamRecordBatchVo.getExamId());
        redisCache.deleteObject(key);
    }


    /**
     * 校验是否正在考试中
     *
     * @param examId
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = CustomException.class)
    public void checkOverExam(long examId) {
        String key = getStartExamKey(examId);
        String dateTime = redisCache.getCacheObject(key);
        if (StringUtils.isEmpty(dateTime)) {
            CmsExamScore cmsExamScore = cmsExamScoreMapper.getScoreByExamIdAndUserid(examId, CmsSecurityUtils.getUserId());
            if (cmsExamScore != null) {
                overExamScore(cmsExamScore);
                throw new CustomException("考试超时，做题无效");
            } else {
                throw new CustomException("考试未开始无法做题");
            }

        }
    }


    /**
     * 开始考试时间
     *
     * @param examId
     * @return
     */
    @Override
    public CmsExamStartVo startExam(long examId) {
        CmsExamStartVo startVo = new CmsExamStartVo();
        String key = getStartExamKey(examId);
        String dateTime = redisCache.getCacheObject(key);
        if (StringUtils.isEmpty(dateTime)) {

            LambdaQueryWrapper<CmsExamExaminee> lambdaQueryWrapper = new LambdaQueryWrapper<CmsExamExaminee>()
                    .eq(CmsExamExaminee::getUserPanId, CmsSecurityUtils.getPanId())
                    .eq(CmsExamExaminee::getExamId, examId);

            Integer mustExam = cmsExamExamineeDao.selectCount(lambdaQueryWrapper);
            if (mustExam == null || mustExam == 0) {
                throw new CustomException("用户不能参加考试");
            }
            CmsExam cmsExam = cmsExamMapper.selectCmsExamById(examId);
            Date nowDate = DateUtils.getNowDate();
            if (nowDate.compareTo(cmsExam.getStartDate()) < 0) {
                throw new CustomException("考试尚未开始");
            }
            if (nowDate.compareTo(cmsExam.getEndDate()) > 0) {
                throw new CustomException("考试已经结束");
            }
            int times = 1;
            CmsExamScore cmsExamScore = cmsExamScoreMapper.getScoreByExamIdAndUserid(examId, CmsSecurityUtils.getUserId());
            if (cmsExamScore != null) {
                if (cmsExamScore.getTimes() != null) {
                    if (cmsExamScore.getTimes() >= cmsExam.getFrequency()) {
                        throw new CustomException("考试次数达到上限！");
                    }
                    times = cmsExamScore.getTimes() + 1;
                }
            }
            int scoreId = initExamScore(examId, times);
            dateTime = DateUtils.getTime();
            startVo.setStartTime(dateTime);
            startVo.setScoreId((long) scoreId);
            redisCache.setCacheObject(key, dateTime, cmsExam.getDuration() + 3, TimeUnit.MINUTES);
        } else {
            startVo.setStartTime(dateTime);
            CmsExamScore cmsExamScore = cmsExamScoreMapper.getScoreByExamIdAndUserid(examId, CmsSecurityUtils.getUserId());
            startVo.setScoreId(cmsExamScore.getId());
        }

        return startVo;
    }

    @Override
    public List<CmsExamReviewVo> selectReviewList(CmsExamReviewParameter parameter) {
        List<CmsExamReviewVo> reviewList = cmsExamScoreMapper.getReviewList(parameter);
        if (CollectionUtils.isNotEmpty(reviewList)) {
            reviewList.forEach(s -> {
                s.setTimesDesc("第" + NumberUtil.int2chineseNum(s.getTimes()) + "次");
            });
        }
        return reviewList;
    }

    @Override
    public List<CmsExamRecordAnswerVo> selectReviewListByUser(CmsExamReviewRequestVo requestVo) {
        return cmsExamScoreMapper.selectReviewListByUser(requestVo);
    }

    @Transactional(rollbackFor = CustomException.class)
    @Override
    public void batchUpdate(CmsExamRecordBatchVo cmsExamRecordBatchVo) {

        CmsExamQuestion cmsExamQuestion = new CmsExamQuestion();
        cmsExamQuestion.setExamId(cmsExamRecordBatchVo.getExamId());
        List<CmsExamQuestion> cmsExamQuestions = cmsExamQuestionMapper.selectCmsExamQuestionList(cmsExamQuestion);
        if (CollectionUtils.isEmpty(cmsExamQuestions)) {
            throw new CustomException("找不到试卷");
        }
        cmsExamRecordBatchVo.getCmsExamRecordList().forEach(s -> {
            s.setUpdateBy(CmsSecurityUtils.getUsername());
            s.setUpdateTime(DateUtils.getNowDate());
            s.setApprover(CmsSecurityUtils.getUserId());
            s.setApproverBy(CmsSecurityUtils.getUsername());
            s.setExamId(cmsExamRecordBatchVo.getExamId());
            s.setScoreId(cmsExamRecordBatchVo.getScoreId());
            s.setStatus(CmsExamRecordStatus.FINISHED.getType());// 已评分
            Optional<CmsExamQuestion> question = cmsExamQuestions.stream()
                    .filter(q -> q.getId().equals(s.getQuestionId()))
                    .findFirst();
            if (question.isPresent()) {
                if (question.get().getScore() < s.getScore()) {
                    throw new CustomException("评分错误，题目:" + question.get().getTitle());
                }
            }
            cmsExamRecordMapper.updateCmsExamRecord(s);
        });
        long sumScore = cmsExamRecordBatchVo.getCmsExamRecordList().stream().mapToLong(s -> s.getScore()).sum();
        CmsExamScore cmsExamScore = cmsExamScoreMapper.selectCmsExamScoreById(cmsExamRecordBatchVo.getScoreId());
        if (cmsExamScore == null) {
            throw new CustomException("未找到用户分数");
        }
        cmsExamScore.setExamScore(sumScore);
        cmsExamScore.setExamStatus(CmsExamScoreStatus.REVIEWED.getStatus());
        cmsExamScore.setUpdateBy(CmsSecurityUtils.getUsername());
        cmsExamScore.setUpdateTime(DateUtils.getNowDate());
        cmsExamScoreMapper.updateCmsExamScore(cmsExamScore);
    }

    @Transactional(rollbackFor = CustomException.class)
    @Override
    public void doRestExam(long examId) {
        cmsExamScoreMapper.deleteCmsExamScoreByExamId(examId);
        cmsExamRecordMapper.deleteCmsExamRecordByExamId(examId);
        Collection<String> keys = redisCache.keys(EXAM_START + examId + "*");
        if (CollectionUtils.isNotEmpty(keys)) {
            redisCache.deleteObject(keys);
        }
    }

    @Transactional(rollbackFor = CustomException.class)
    @Override
    public void correctionCompleted(long examId) {
        CmsExam cmsExam = cmsExamMapper.selectCmsExamById(examId);
        cmsExam.setStatus(3L);
        cmsExamMapper.updateCmsExam(cmsExam);
        List<Long> userIds = cmsExamRecordMapper.selectCmsExamRecordByEaxmId(examId);
        LambdaQueryWrapper<CmsExamExaminee> lambdaQueryWrapper;
        if (CollectionUtils.isNotEmpty(userIds)) {
            cmsExamRecordMapper.updateCmsExamRecordComplete(examId);
            lambdaQueryWrapper = new LambdaQueryWrapper<CmsExamExaminee>()
                    .eq(CmsExamExaminee::getExamId, examId)
                    .notIn(CmsExamExaminee::getUserPanId, userIds);
        }else{
            lambdaQueryWrapper = new LambdaQueryWrapper<CmsExamExaminee>()
                    .eq(CmsExamExaminee::getExamId, examId);
        }


            List<CmsExamExaminee> examineeInfos = cmsExamExamineeDao.selectList(lambdaQueryWrapper);
            if (CollectionUtils.isNotEmpty(examineeInfos)) {
                examineeInfos.stream().forEach(s -> {
                    CmsExamScore score = new CmsExamScore();
                    score.setExamId(examId);
                    score.setCreateBy(CmsSecurityUtils.getUsername());
                    score.setCreateTime(DateUtils.getNowDate());
                    score.setExamScore(0L);
                    score.setTimes(1);
                    score.setExamStatus(CmsExamScoreStatus.REVIEWED.getStatus());
                    score.setRemark("手动完成批改");
                    score.setUserId(s.getUserPanId());
                    cmsExamScoreMapper.insertCmsExamScore(score);
                });
            }

    }

    @Transactional(rollbackFor = CustomException.class)
    @Override
    public void replicate(long examId) {
        CmsExam exam = cmsExamMapper.selectCmsExamById(examId);
        exam.setId(0L);
        exam.setStatus(0L);
        exam.setName(exam.getName() + "_复制");

        List<CmsExamQuestion> list = cmsExamQuestionMapper.selectCmsExamQuestionByExamId(examId);
        if (CollectionUtils.isNotEmpty(list)) {
            list.stream().forEach(s -> s.setId(0L));
            exam.setCmsExamQuestionList(list);
        }
        LambdaQueryWrapper<CmsExamExaminee> lambdaQueryWrapper = new LambdaQueryWrapper<CmsExamExaminee>()
                .eq(CmsExamExaminee::getExamId, examId);
        long newExamId = iCmsExamService.insertCmsExam(exam);
        List<CmsExamExaminee> cmsExamExaminees = cmsExamExamineeDao.selectList(lambdaQueryWrapper);
        cmsExamExaminees.stream().forEach(s -> {
            s.setExamId(newExamId);
            cmsExamExamineeDao.insert(s);
        });
    }

    private String getStartExamKey(long examId) {
        return EXAM_START + examId + "_" + CmsSecurityUtils.getUserId();
    }

    /**
     * 初始化分数
     *
     * @param examId
     * @return
     */
    private int initExamScore(long examId, int times) {
        CmsExamScore score = new CmsExamScore();
        score.setExamId(examId);
        score.setCreateBy(CmsSecurityUtils.getUsername());
        score.setCreateTime(DateUtils.getNowDate());
        score.setExamScore(-1L);
        score.setTimes(times);
        score.setExamStatus(CmsExamScoreStatus.START.getStatus());
        score.setRemark("开始考试");
        score.setUserId(CmsSecurityUtils.getUserId());
        return cmsExamScoreMapper.insertCmsExamScore(score);
    }

    private void overExamScore(CmsExamScore cmsExamScore) {
        cmsExamScore.setExamScore(0L);
        cmsExamScore.setExamStatus(CmsExamScoreStatus.REVIEWED.getStatus());
        cmsExamScore.setRemark("考试超时，做题无效");
        cmsExamScore.setUpdateBy(CmsSecurityUtils.getUsername());
        cmsExamScore.setUpdateTime(DateUtils.getNowDate());
        cmsExamScoreMapper.updateCmsExamScore(cmsExamScore);
    }
}
